package com.haoqizhe.kernel.shiro.jwt;

import cn.hutool.core.util.StrUtil;
import com.haoqizhe.kernel.jwt.utils.JwtTokenUtil;
import com.haoqizhe.kernel.redis.common.util.RedisUtil;
import com.haoqizhe.kernel.shiro.properties.ShiroProperties;
import com.haoqizhe.kernel.shiro.userdetails.UserInfoService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;

import java.util.List;

/**
 * @author haoqizhe.li
 * @date 2018/7/6
 **/
@Slf4j
public class JwtRealm extends AuthorizingRealm {

    @Autowired
    @Lazy
    private UserInfoService userInfoServiceImpl;

    private ShiroProperties shiroProperties;

    private RedisUtil redisUtil;

    private JwtTokenUtil jwtTokenUtil;

    public JwtRealm(ShiroProperties shiroProperties, RedisUtil redisUtil, JwtTokenUtil jwtTokenUtil) {
        this.shiroProperties = shiroProperties;
        this.redisUtil = redisUtil;
        this.jwtTokenUtil = jwtTokenUtil;
    }

    @Override
    public boolean supports(AuthenticationToken token) {
        return token != null && token instanceof JwtToken;
    }

    /**
     * &
     * 用户授权
     *
     * @param principal
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        log.debug("================权限注册==========");

        JwtToken jwtToken = (JwtToken) principal.getPrimaryPrincipal();

        /**
         * 用户角色
         */
        List<String> roles = userInfoServiceImpl.getRoleOfUser(jwtToken.getUsername());
        authorizationInfo.addRoles(roles);

        /**
         * 用户接口权限，菜单权限
         */
        List<String> permissions = userInfoServiceImpl.getPermissionsOfUser(jwtToken.getUsername());
        authorizationInfo.addStringPermissions(permissions);
        return authorizationInfo;
    }

    /**
     * 用户认证
     *
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) {

        log.debug("=============用户认证==============");

        JwtToken jwtToken = (JwtToken) authenticationToken;
        if (jwtToken == null) {
            throw new AuthenticationException("jwtToken is empty");
        }
        if (StrUtil.isBlank(jwtToken.getSalt())) {
            throw new AuthenticationException("salt is empty");
        }

        String token = jwtToken.getToken();
        if (StrUtil.isBlank(token)) {
            throw new AuthenticationException("token is empty");
        }

        if (jwtTokenUtil.isExpiration(token)) {
            throw new AuthenticationException("token is expired");
        }

        String username = jwtToken.getUsername();
        if (StrUtil.isBlank(username)) {
            throw new AuthenticationException("username is empty");
        }

        if (shiroProperties.getSingleLogin()) {
            JwtRedis jwtRedis = new JwtRedis(redisUtil, shiroProperties, jwtTokenUtil);
            if (!jwtRedis.setToken(token, username)) {
                throw new AuthenticationException("login: redis set token error");
            }
            jwtRedis.setUser(username, token);
        }

        return new SimpleAuthenticationInfo(
                jwtToken,
                ByteSource.Util.bytes(jwtToken.getSalt()),
                getName());
    }
}
